// This File is part of the "nyaqt" Project
// 
// Copyright  2009 sometmes@gmail.com
// All rights reserved
// 
// This library is free software; you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
// 
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;

using System.Net.Mail;
using System.Net;
using System.IO;
using System.Runtime.InteropServices;
using System.Drawing.Imaging;

namespace Notify2
{
    public partial class SendMailForm : Form
    {
        SmtpClient smtp;

        const string TITLE = "Send to Gmail  {0}";
        const int MAXFILESIZE = 25 * 1024 * 1024;
        //const int MAXFILESIZE = (int)(1.5F * 1024 * 1024);
        List<AlternateFileStream> attachments;
        AlternateFileStream currentFile;
        bool finishedSending;
        AlternateFileStream displayedFile;
        int finishedFilesSize;
        int filesSize;
        int endmessagesize;
        List<MailMessage> messages;
        MailMessage currentMessage;
        object SyncRoot = new int();
        bool Sending = false;
        string from;

        public SendMailForm()
        {
            InitializeComponent();
        }

        private void InitSmtpClient()
        {
            attachments = new List<AlternateFileStream>();
            messages = new List<MailMessage>();
            currentMessage = null;
            currentFile = null;
            displayedFile = null;
            finishedFilesSize = 0;
            filesSize = 0;
            endmessagesize = 0;
            progressBar1.Value = 0;
            this.Text = string.Format(TITLE, "");

            smtp = new SmtpClient();
            smtp.Host = "smtp.gmail.com";
            smtp.Port = 587;
            smtp.EnableSsl = true;
            smtp.DeliveryMethod = SmtpDeliveryMethod.Network;
            smtp.Credentials = new NetworkCredential(Program.OptionsForm.loginTextBox.Text, Program.OptionsForm.pwdTextBox.Text);
            smtp.SendCompleted += new SendCompletedEventHandler(smtp_SendCompleted);
            this.from = Program.OptionsForm.loginTextBox.Text;
            if (!this.from.Contains("@"))
                this.from += "@gmail.com";
        }

        internal void AddFile(string fileName)
        {
            lock (SyncRoot)
            {
                if (Sending) return;

                addFileTimeoutTimer.Stop();
                AddAttachment(fileName);
                this.Text = string.Format(TITLE, "File 1/" + attachments.Count);
                CheckInitialUI();
                this.Show();
                addFileTimeoutTimer.Start();
            }
        }

        private void CheckInitialUI()
        {
            if (this.attachments.Count == 1)
            {
                DisplayFile(this.attachments[0]);
                this.Text = string.Format(TITLE, "File 1/" + attachments.Count);
            }
        }

        private void SendMailForm_Load(object sender, EventArgs e)
        {
            string login = Program.OptionsForm.loginTextBox.Text;
            if (login.Contains("@"))
                loginLabel.Text = "To: " + login;
            else
                loginLabel.Text = "To: " + login + "@gmail.com";

            pictureBox1.Visible = true;
            pictureBox2.Visible = false;

            InitSmtpClient();

            if (Program.sequenceMutex!= null)
                Program.sequenceMutex.ReleaseMutex();
            string[] args = Environment.GetCommandLineArgs();
            if (args.Length > 2)
                AddFile(args[2]);
        }

        private void SendMailForm_Shown(object sender, EventArgs e)
        {
            //Hide();
        }

        void Send()
        {
            this.endmessagesize = this.filesSize * 10 / 100;
            progressBar1.Maximum = this.filesSize + this.endmessagesize * this.messages.Count;
            progressBar1.Minimum = 0;
            this.finishedSending = false;
            this.currentFile = this.attachments[0];
            this.currentMessage = this.messages[0];
            progressTimer.Enabled = true;
            smtp.SendAsync(this.currentMessage, null);
        }

        void AddAttachment(string filename)
        {
            AlternateFileStream reader;
            reader = new AlternateFileStream(filename, FileMode.Open, FileAccess.Read);
            string[] banned = new string[] { ".exe", ".zip" };
            if (-1 != Array.IndexOf<string>(banned, Path.GetExtension(filename).ToLower()))
                reader.AlternateName = filename + "file";
            if (reader.Length >= MAXFILESIZE)
            {
                throw new ApplicationException("File size exceeds gmail maximum allowed (25MB): " + filename);
            }
            this.attachments.Add(reader);
        }

        void smtp_SendCompleted(object sender, AsyncCompletedEventArgs e)
        {
            finishedFilesSize+=this.endmessagesize;
            progressTimer_Tick(null, null);

            if (e.Error != null)
                MessageBox.Show(e.Error.ToString());
            else
            {
                int messagenum = messages.IndexOf(currentMessage);
                if (messagenum == messages.Count - 1)
                {
                    progressBar1.Value = progressBar1.Maximum;
                    foreach (AlternateFileStream file in attachments)
                    {
                        file.Close();
                        if (file.ToBeDeleted)
                            File.Delete(file.Name);
                    }
                    MessageBox.Show("Finished," + this.messages.Count + " messages sent.","Send To GMail",MessageBoxButtons.OK,MessageBoxIcon.Information);

                    if (Program.StayInTray)
                        this.Hide();
                    else
                        Application.Exit();
                }
                else
                {
                    this.currentMessage = this.messages[messagenum + 1];
                    smtp.SendAsync(this.currentMessage, null);
                    progressTimer.Enabled = true;
                }
            }


        }

        private void progressTimer_Tick(object sender, EventArgs e)
        {
            progressTimer.Enabled = false;
            while (!this.finishedSending && this.currentFile.Position == this.currentFile.Length)
            {
                this.finishedFilesSize += (int)this.currentFile.Length;
                int i = this.attachments.IndexOf(this.currentFile);
                if (i == this.attachments.Count - 1)
                {
                    this.finishedSending = true;
                    break;
                }
                this.currentFile = this.attachments[i + 1];
            }

            if (this.displayedFile != this.currentFile && this.currentFile.Position > 0)
            {
                DisplayFile(this.currentFile);
                this.displayedFile = this.currentFile;
            }

            int fileIndexToShow = attachments.IndexOf(this.currentFile);
            if (this.currentFile.Position == 0)
                fileIndexToShow -= 1;
            this.Text = string.Format(TITLE, "File " + (fileIndexToShow + 1) + "/" + attachments.Count + " Message " + (this.messages.IndexOf(this.currentMessage) + 1) + "/" + this.messages.Count);

            if (this.finishedSending)
                progressBar1.Value = (int)this.finishedFilesSize;
            else
                progressBar1.Value = (int)(this.finishedFilesSize + this.currentFile.Position);
            if (sender != null)
                progressTimer.Enabled = true;
        }

        private void DisplayFile(AlternateFileStream file)
        {
            filenameLabel.Text = Path.GetFileName(file.AlternateName);
            string foldername = Path.GetDirectoryName(file.AlternateName);
            folderLabel.Text = Path.GetFileName(foldername);
            if (folderLabel.Text == "")
                folderLabel.Text = foldername;

            int size = (int)file.Length;
            if (size < 1024)
                fileSizeLabel.Text = size + " B";
            else if (size < 1024 * 1024)
                fileSizeLabel.Text = (size / 1024F) + " KB";
            else if (size < 1024 * 1024 * 1024)
                fileSizeLabel.Text = (size / 1024F / 1024F) + " MB";

            fileModifiedLabel.Text = File.GetLastWriteTime(file.AlternateName).ToShortDateString();

            string ext = Path.GetExtension(file.AlternateName).ToLower();
            string[] images = new string[] { ".jpg", ".gif", ".png", ".bmp", ".tif", ".tiff" };
            if (Array.IndexOf<string>(images, ext) >= 0)
            {
                if (pictureBox1.Image != null) pictureBox1.Image.Dispose();
                Image img = Bitmap.FromFile(file.AlternateName);
                Bitmap th = ResizeHightQualityMantainAspectRatio(img, pictureBox1.ClientSize.Width, pictureBox1.ClientSize.Height);
                img.Dispose();
                pictureBox1.Image = th;
                pictureBox2.Image = null;
                pictureBox2.Visible = false;
                pictureBox1.Visible = true;
            }
            else
            {
                if (pictureBox2.Image != null) pictureBox2.Image.Dispose();
                pictureBox2.Image = System.Drawing.Icon.ExtractAssociatedIcon(file.Name).ToBitmap();
                pictureBox1.Image = null;
                pictureBox1.Visible = false;
                pictureBox2.Visible = true;
            }
        }

        private void addFileTimeoutTimer_Tick(object sender, EventArgs e)
        {
            lock (SyncRoot)
            {
                this.addFileTimeoutTimer.Stop();
                this.Sending = true;
                if (CheckOptions() == DialogResult.OK)
                {
                    ResizePhotos();
                    PrepareMessages();
                    Send();
                }
                else
                    this.Close();
            }
        }

        private static ImageCodecInfo GetEncoderInfo(String mimeType)
        {
            int j;
            ImageCodecInfo[] encoders;
            encoders = ImageCodecInfo.GetImageEncoders();
            for (j = 0; j < encoders.Length; ++j)
            {
                if (encoders[j].MimeType == mimeType)
                    return encoders[j];
            } return null;
        }

        Bitmap ResizeHightQualityMantainAspectRatio(Image img, int width, int height)
        {
            Size newSize = new Size();
            float percentW = ((float)width / (float)img.Width);
            float percentH = ((float)height / (float)img.Height);
            float percent = Math.Min(percentH, percentW);
            newSize.Width = (int)(img.Width * percent);
            newSize.Height = (int)(img.Height * percent);

            Bitmap bmp = new Bitmap(newSize.Width, newSize.Height);
            Graphics g = Graphics.FromImage(bmp);
            g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
            g.DrawImage(img, 0, 0, newSize.Width, newSize.Height);
            g.Dispose();
            return bmp;
        }

        void ResizePhotos()
        {
            if (!optionsForm.AskForImages) return;

            ImageCodecInfo jpegCodec = GetEncoderInfo("image/jpeg");
            System.Drawing.Imaging.Encoder quality = System.Drawing.Imaging.Encoder.Quality;
            if (this.optionsForm.KeepOriginalSize) return;
            int selectedWidth = optionsForm.SelectedImageSize.Width;
            int selectedHeight = optionsForm.SelectedImageSize.Height;
            for (int i = 0; i < this.attachments.Count; i++)
            {
                AlternateFileStream reader = this.attachments[i];
                if (Array.IndexOf<string>(jpg, Path.GetExtension(reader.AlternateName).ToLower()) >= 0)
                {
                    Image img = Image.FromStream(reader);
                    Bitmap bmp = ResizeHightQualityMantainAspectRatio(img, selectedWidth, selectedHeight);
                    foreach (PropertyItem prop in img.PropertyItems)
                    {
                        bmp.SetPropertyItem(prop);
                    }
                    string tmp = Path.GetTempFileName();
                    EncoderParameters encoderParams = new EncoderParameters(1);
                    encoderParams.Param[0] = new EncoderParameter(quality, (long)85); ;
                    bmp.Save(tmp, jpegCodec, encoderParams);
                    AlternateFileStream stream = new AlternateFileStream(tmp, FileMode.Open, FileAccess.Read);
                    stream.AlternateName = reader.Name;
                    stream.ToBeDeleted = true;
                    attachments.RemoveAt(i);
                    attachments.Insert(i, stream);
                    img.Dispose();
                    bmp.Dispose();
                }
            }
        }

        //[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        //internal static extern bool CreateDirectory(string path, object lpSecurityAttributes);
        //const int ERROR_ALREADY_EXISTS = 183;

        //private void CreateTempDir()
        //{
        //    string tmp = Path.GetTempPath();
        //    int cont = 0;
        //    while (true)
        //    {
        //        this.tempDir = Path.Combine(tmp, "send2gmail" + cont);
        //        if (!CreateDirectory(this.tempDir, null))
        //        {
        //            int err = Marshal.GetLastWin32Error();
        //            if (err != ERROR_ALREADY_EXISTS)
        //                throw new SystemException(err.ToString());
        //        }
        //        cont+=1;
        //    }
        //}

        private void PrepareMessages()
        {
            int aggregatedsize = 0;
            MailMessage msg = new MailMessage(this.from, this.from, "Files sent", "");
            messages.Add(msg);
            for (int i = 0; i < attachments.Count; i++)
            {
                AlternateFileStream reader = attachments[i];
                this.filesSize += (int)reader.Length;
                aggregatedsize += (int)reader.Length;
                if (aggregatedsize >= MAXFILESIZE)
                {
                    msg = new MailMessage(this.from, this.from, "Files sent", "");
                    aggregatedsize = 0;
                    messages.Add(msg);
                }
                msg.Attachments.Add(new Attachment((Stream)reader, Path.GetFileName(reader.AlternateName)));
                msg.Body += Path.GetFileName(reader.AlternateName) + "\r\n";
            }
            int msgcount = messages.Count;
            int msgiter = 1;
            foreach (MailMessage msg2 in messages)
            {
                string subject = msg2.Attachments.Count + " Files received";
                if (msgcount > 1)
                    subject += ". Message " + msgiter + " of " + msgcount;
                msg2.Subject = subject;
                msgiter += 1;
            }
        }

        SendToGmailOptionsForm optionsForm;

        string[] jpg = new string[] { ".jpg", ".jpeg" };
        private DialogResult CheckOptions()
        {
            optionsForm = new SendToGmailOptionsForm();

            //if (filesSize > MAXFILESIZE)
            //    optionsForm.AskForMultipleEmail = true;

            optionsForm.AskForImages = false;
            foreach (AlternateFileStream file in attachments)
            {
                if (Array.IndexOf<string>(jpg, Path.GetExtension(file.AlternateName).ToLower()) >= 0)
                {
                    optionsForm.AskForImages = true;
                    break;
                }
            }

            if (optionsForm.AskForImages)
                return optionsForm.ShowDialog();
            else
                return DialogResult.OK;
        }

        private void SendMailForm_FormClosing(object sender, FormClosingEventArgs e)
        {
            progressTimer.Stop();
            smtp.SendCompleted -= new SendCompletedEventHandler(smtp_SendCompleted);
            smtp.SendAsyncCancel();

            if (!Program.StayInTray)
                Application.Exit();

            //Sending = false;
            //InitSmtpClient();
            //if (e.CloseReason == CloseReason.UserClosing)
            //{
            //    this.Hide();
            //    e.Cancel = true;
            //}
        }

        private void SendMailForm_MouseDoubleClick(object sender, MouseEventArgs e)
        {
            addFileTimeoutTimer.Interval = 1;
        }
    }
}
